//
//  FAudioAnalyzer.m
//  FFoundation
//
//  Created by 新庭 on 2020/8/1.
//

#import "FAudioAnalyzer.h"
@import Accelerate;

@interface FAudioAnalyzer ()

@property (nonatomic, assign) FFTSetup fftSetup;

@property (nonatomic, assign) NSInteger freqNum;
@property (nonatomic, assign) NSInteger freqStart;
@property (nonatomic, assign) NSInteger freqEnd;

@end

static NSInteger fftSize = 2048;
@implementation FAudioAnalyzer

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.fftSetup = vDSP_create_fftsetup((vDSP_Length)((round(log2(fftSize)))), kFFTRadix2);
        
        self.freqNum = 80;
        self.freqStart = 100;
        self.freqEnd = 18000;
    }
    return self;
}

- (void)dealloc
{
    vDSP_destroy_fftsetup(self.fftSetup);
}

- (NSArray *)generateBands {
//    var bands = [(lowerFrequency: Float, upperFrequency: Float)]()
    NSMutableArray *mArr = [NSMutableArray arrayWithCapacity:0];
    //1：根据起止频谱、频带数量确定增长的倍数：2^n
    NSInteger n = log2(self.freqEnd / self.freqStart) / self.freqNum;
//    var nextBand: (lowerFrequency: Float, upperFrequency: Float) = (startFrequency, 0)
    NSMutableDictionary *nextBand = [NSMutableDictionary dictionaryWithDictionary:@{
        @0: @(self.freqStart),
        @1: @0
    }];
    
    for (int i = 0; i < self.freqNum; i++) {
        //2：频带的上频点是下频点的2^n倍
        CGFloat highFrequency = [nextBand[@0] floatValue] * powf(2, n);
        nextBand[@1] = i == self.freqNum - 1 ? @(self.freqEnd) : @(highFrequency);
        [mArr addObject:[nextBand mutableCopy]];
        nextBand[@0] = @(highFrequency);
    }
    return mArr;
}

- (NSArray<NSArray<NSNumber *> *> *)analyse:(AVAudioPCMBuffer *)buffer {
    NSArray<NSArray *> *datas = [self analyseFFT:buffer];
    
    NSArray *bands = [self generateBands];
    
    NSMutableArray *mArr = [NSMutableArray arrayWithCapacity:datas.count];
    for (NSArray<NSNumber *> *channelAmplitudes in datas) {
        NSMutableArray *spectrum = [NSMutableArray array];
        for (NSDictionary *dic in bands) {
            CGFloat start = [dic[@0] floatValue];
            CGFloat end = [dic[@1] floatValue];
            
            CGFloat max = [self findMaxFrom:start to:end at:channelAmplitudes with:buffer.format.sampleRate / fftSize];
            [spectrum addObject:@(max)];
        }
        [mArr addObject:spectrum];
    }
    return mArr;
}

- (CGFloat)findMaxFrom:(CGFloat)start to:(CGFloat)end at:(NSArray<NSNumber *> *)datas with:(CGFloat)bandWidth {
    return 0;
}

- (NSArray<NSArray<NSNumber *> *> *)analyseFFT:(AVAudioPCMBuffer *)buffer {
    NSInteger channelCount = buffer.format.channelCount;
    float * const *channels = buffer.floatChannelData;
    BOOL isInterleaved = buffer.format.isInterleaved;
    
    if (isInterleaved) {
        
    }
    
    NSMutableArray *amplitudes = [NSMutableArray arrayWithCapacity:channelCount];
    
    for (NSInteger i = 0; i < channelCount; i++) {
        float * channel = channels[i];
        
        float *window = (float *)malloc(fftSize * sizeof(float));
        
        vDSP_hann_window(window, fftSize, vDSP_HANN_NORM);
        vDSP_vmul(channel, 1, window, 1, channel, 1, fftSize);
        
        long complexSize = fftSize / 2;
        Float32 * realp = (Float32 *)malloc(sizeof(Float32) * complexSize);
        Float32 * imagp = (Float32 *)malloc(sizeof(Float32) * complexSize);
        DSPSplitComplex fftInOut = {realp, imagp};
        
        vDSP_fft_zrip(self.fftSetup, &fftInOut, 1, round(log2(fftSize)), FFT_FORWARD);
        
        //5：调整FFT结果，计算振幅
        fftInOut.imagp[0] = 0;
        float fftNormFactor = 1.0 / (fftSize);
        vDSP_vsmul(fftInOut.realp, 1, &fftNormFactor, fftInOut.realp, 1, complexSize);
        vDSP_vsmul(fftInOut.imagp, 1, &fftNormFactor, fftInOut.imagp, 1, complexSize);
        float * channelAmplitudes = (float *)malloc(sizeof(float) * complexSize);
        
        vDSP_zvabs(&fftInOut, 1, channelAmplitudes, 1, complexSize);
        channelAmplitudes[0] = channelAmplitudes[0] / 2; //直流分量的振幅需要再除以2
        
        NSMutableArray *caArr = [NSMutableArray arrayWithCapacity:complexSize];
        for (int i = 0; i < complexSize; i++) {
            [caArr addObject:@(channelAmplitudes[i])];
        }
        [amplitudes addObject:caArr];
    }
    
    return amplitudes;
}

@end
